//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
/******************************************************************************
**
** INTEL CONFIDENTIAL
** Copyright 2003-2005 Intel Corporation All Rights Reserved.
**
** The source code contained or described herein and all documents
** related to the source code (Material) are owned by Intel Corporation
** or its suppliers or licensors.  Title to the Material remains with
** Intel Corporation or its suppliers and licensors. The Material contains
** trade secrets and proprietary and confidential information of Intel
** or its suppliers and licensors. The Material is protected by worldwide
** copyright and trade secret laws and treaty provisions. No part of the
** Material may be used, copied, reproduced, modified, published, uploaded,
** posted, transmitted, distributed, or disclosed in any way without Intel's
** prior express written permission.
**
** No license under any patent, copyright, trade secret or other intellectual
** property right is granted to or conferred upon you by disclosure or
** delivery of the Materials, either expressly, by implication, inducement,
** estoppel or otherwise. Any license under such intellectual property rights
** must be express and approved by Intel in writing.
**
******************************************************************************/

/****************************************************************
*    FAT.c
*       This is the FAT fs reader that is used with eboot
*       to read and copy images off of SD/MMC cards.
*
*    Revision History:
*       Jason White - 1/18/06 - initial draft
*       Jason White - 2/03/06 - added some functions
*       Jason White - 2/06/06 - added comments
****************************************************************/

#ifndef NO_NH040526A_LOGGING
#ifndef ENABLE_DEBUG_MESSAGES
#define ENABLE_DEBUG_MESSAGES 1
#endif

#ifndef ENABLE_LOG_MESSAGES
#define ENABLE_LOG_MESSAGES 1
#endif

#if ENABLE_DEBUG_MESSAGES
#define DBGMSG(cond, pargs) NKDbgPrintfW pargs
#else
#define DBGMSG(cond, pargs) 0
#endif
#if ENABLE_LOG_MESSAGES
#define LOGMSG(cond, pargs) NKDbgPrintfW pargs 
#else
#define LOGMSG(cond, pargs) 0
#endif

#define LOGGING_ENABLED (ENABLE_DEBUG_MESSAGES || ENABLE_LOG_MESSAGES)
#if LOGGING_ENABLED
#include <windows.h>   // for NKDbgPrintfW()
#endif
#endif /* NH040526A_LOGGING */

#include "FAT.h"

/******************************************************************************
*   fillBootBlockStructure
*      Sets up a structure containing all of the boot sector information
*   Input:
*      pBPB - the structure to be filled
*      buffer - the data read off of the card
*   Output:
*      pBPB has been filled with the boot sector data
*   Returns:
*      TRUE if it is a boot block
*      FALSE if it isnt a boot block
******************************************************************************/
BOOL fillBootBlockStructure(P_BPB_STRUCTURE pBPB, BYTE *buffer)
{
    int i, offset;

    if(buffer[0] != 0xEB && buffer[0] != 0xE9)
        return FALSE;

    for (i=0; i<3; i++)
        pBPB->BS_jmpBoot[i] = (BYTE)buffer[i];

    offset = 3;

    for (i = 0; i < 8; i++)
        pBPB->BS_OEMName[i] = (BYTE)buffer[i+offset];

    offset+=8;

    for (i=0; i<2; i++)
        pBPB->BPB_BytsPerSec[i] = (BYTE)buffer[i+offset];

    offset+=2;

    pBPB->BPB_SecPerClus = (BYTE)buffer[offset];

    offset++;

    for (i = 0; i < 2; i++)
        pBPB->BPB_RsvdSecCnt[i] = (BYTE) buffer[i + offset];

    offset += 2;

    pBPB->BPB_NumFATs = (BYTE) buffer[offset];

    offset++;

    for (i = 0; i < 2; i++)
        pBPB->BPB_RootEntCnt[i] = (BYTE) buffer[i + offset];

    offset += 2;

    for(i=0; i<2; i++)
        pBPB->BPB_TotSec16[i] = (BYTE)buffer[i+offset];

    offset+=2;

    pBPB->BPB_Media = (BYTE)buffer[offset];

    offset++;

    for(i=0; i<2; i++)
        pBPB->BPB_FATSz16[i] = (BYTE)buffer[i+offset];

    offset+=2;

    for(i=0; i<2; i++)
        pBPB->BPB_SecPerTrk[i] = (BYTE)buffer[i+offset];

    offset+=2;

    for(i=0; i<2; i++)
        pBPB->BPB_NumHeads[i] = (BYTE)buffer[i+offset];

    offset+=2;

    for(i=0; i<4; i++)
        pBPB->BPB_HiddSec[i] = (BYTE)buffer[i+offset];

    offset+=4;

    for(i=0; i<4; i++)
        pBPB->BPB_TotSec32[i] = (BYTE)buffer[i+offset];

    offset+=4;

    pBPB->BPB_DrvNum = (BYTE)buffer[offset];

    offset++;

    pBPB->BPB_Reserved1 = (BYTE)buffer[offset];

    offset++;

    pBPB->BPB_BootSig = (BYTE)buffer[offset];

    offset++;

    for(i=0; i<4; i++)
        pBPB->BPB_VolID[i] = (BYTE)buffer[i+offset];

    offset+=4;

    for(i=0; i<11; i++)
        pBPB->BPB_VolLab[i] = (BYTE)buffer[i+offset];
	
    offset+=11;

    for(i=0; i<8; i++)
        pBPB->BPB_FilSysType[i] = (BYTE)buffer[i+offset];

    return TRUE;
}

/******************************************************************************
*   getFATDataStructure
*      Sets up a structure containing all of the important FAT fs information
*   Input:
*      pBPB - the boot sector structure
*      pFAT - the FAT data structure to be filled
*   Output:
*      pFAT has been filled with the important FAT fs information
*   Returns:
*      none
******************************************************************************/
void getFATDataStructure(P_BPB_STRUCTURE pBPB, P_FAT_STRUCTURE pFAT)
{
    XLLP_UINT32_T RootEntCnt, BytsPerSec, FATSz16; 
    XLLP_UINT32_T TotSec16, TotSec32, RsvdSecCnt;
    RootEntCnt = pBPB->BPB_RootEntCnt[1];
    RootEntCnt = RootEntCnt << 8;
    RootEntCnt |= pBPB->BPB_RootEntCnt[0];

    BytsPerSec = pBPB->BPB_BytsPerSec[1];
    BytsPerSec = BytsPerSec << 8;
    BytsPerSec |= pBPB->BPB_BytsPerSec[0];

    FATSz16 = pBPB->BPB_FATSz16[1];
    FATSz16 = FATSz16 << 8;
    FATSz16 |= pBPB->BPB_FATSz16[0];

    TotSec16 = pBPB->BPB_TotSec16[1];
    TotSec16 = TotSec16 << 8;
    TotSec16 |= pBPB->BPB_TotSec16[0];

    TotSec32 = pBPB->BPB_TotSec32[3];
    TotSec32 = TotSec32 << 8;
    TotSec32 |= pBPB->BPB_TotSec32[2];
    TotSec32 = TotSec32 << 8;
    TotSec32 |= pBPB->BPB_TotSec32[1];
    TotSec32 = TotSec32 << 8;
    TotSec32 |= pBPB->BPB_TotSec32[0];

    RsvdSecCnt = pBPB->BPB_RsvdSecCnt[1];
    RsvdSecCnt = RsvdSecCnt << 8;
    RsvdSecCnt |= pBPB->BPB_RsvdSecCnt[0];

    pFAT->RootDirsSectors = ((RootEntCnt * 32) + (BytsPerSec - 1)) / BytsPerSec;

    pFAT->FATSz = FATSz16;

    pFAT->FirstRootDirSecNum = 
        RsvdSecCnt + ((XLLP_UINT32_T)pBPB->BPB_NumFATs * pFAT->FATSz);

    pFAT->FirstDataSector = pFAT->FirstRootDirSecNum + pFAT->RootDirsSectors;

    if(TotSec16 != 0)
        pFAT->TotSec = TotSec16;
    else
        pFAT->TotSec = TotSec32;

    pFAT->DataSec = pFAT->TotSec - pFAT->FirstDataSector;

    pFAT->CountOfClusters = pFAT->DataSec / (XLLP_UINT32_T)pBPB->BPB_SecPerClus;

    if(pFAT->CountOfClusters < 4085)
        pFAT->FATType = FAT12;
    else if(pFAT->CountOfClusters < 65525)
        pFAT->FATType = FAT16;
    else
        pFAT->FATType = FAT32;

//	DumpFATStructure(pFAT);
}

/******************************************************************************
*   getSectorOffset
*      Given a cluster it determines where it is found in the FAT
*   Input:
*      pBPB - the structure containing boot sector information
*      pFAT - the structure containing the important FAT fs information
*      pReadFAT - the structure to be filled
*      cluster - the cluster number to be located in the FAT
*   Output:
*      pReadFAT has been filled with the location and offset of the cluster in 
*      the FAT.
*   Returns:
*      none
******************************************************************************/
void getSectorOffset(P_BPB_STRUCTURE pBPB, P_FAT_STRUCTURE pFAT, 
					 P_READ_FAT pReadFAT, XLLP_UINT32_T cluster)
{
    XLLP_UINT32_T BytsPerSec, RsvdSecCnt, FATOffset;

    BytsPerSec = pBPB->BPB_BytsPerSec[1];
    BytsPerSec = BytsPerSec << 8;
    BytsPerSec |= pBPB->BPB_BytsPerSec[0];

    RsvdSecCnt = pBPB->BPB_RsvdSecCnt[1];
    RsvdSecCnt = RsvdSecCnt << 8;
    RsvdSecCnt |= pBPB->BPB_RsvdSecCnt[0];

    if(pFAT->FATType == FAT16)
        FATOffset = cluster * 2;
    else if(pFAT->FATType == FAT32)
        FATOffset = cluster * 4;

    pReadFAT->ThisFATSecNum = RsvdSecCnt + (FATOffset / BytsPerSec);
    pReadFAT->ThisFATEntOffset = FATOffset % BytsPerSec;
}

/******************************************************************************
*   isEOF
*      Determines if a given sector is the end of a file
*   Input:
*      FATType - the type of FAT (FAT16, FAT32)
*      sector - the sector to be checked
*   Output:
*      none
*   Returns:
*      TRUE if it is the end of a file
*      FALSE if it isnt the end of a file
******************************************************************************/
BOOL isEOF(XLLP_UINT32_T FATType, XLLP_UINT32_T sector)
{
    if(FATType == FAT16)
        if(sector >= END_OF_FILE)
            return TRUE;
    return FALSE;
}

/******************************************************************************
*   getShortDirStructure
*      Sets up a structure for a short directory entry
*   Input:
*      pDIR - the structure to be filled
*      buffer - the data from the block read
*   Output:
*      pDIR has been filled with the directory entry information.
*   Returns:
*      none
******************************************************************************/
void getShortDirStructure(P_DIR_STRUCTURE pDIR, BYTE *buffer)
{
    int i, offset;

    if(buffer[0] != 0xE5)
    {
        for(i=0; i<11; i++)
        {
            pDIR->DIR_Name[i] = buffer[i];
            if(pDIR->DIR_Name[0] == 0)
                return;
        }
    }

    pDIR->DIR_Attr = buffer[11];
    pDIR->DIR_NTRes = buffer[12];
    pDIR->DIR_CrtTimeTenth = buffer[13];

    offset = 14;

    for(i=0; i<2; i++)
        pDIR->DIR_CrtTime[i] = buffer[i+offset];

    offset = 16;

    for(i=0; i<2; i++)
        pDIR->DIR_CrtDate[i] = buffer[i+offset];

    offset = 18;

    for(i=0; i<2; i++)
        pDIR->DIR_LstAccDate[i] = buffer[i+offset];

    offset = 20;

    for(i=0; i<2; i++)
        pDIR->DIR_FstClusHI[i] = buffer[i+offset];

    offset = 22;

    for(i=0; i<2; i++)
        pDIR->DIR_WrtTime[i] = buffer[i+offset];

    offset = 24;

    for(i=0; i<2; i++)
        pDIR->DIR_WrtDate[i] = buffer[i+offset];

    offset = 26;

    for(i=0; i<2; i++)
        pDIR->DIR_FstClusLO[i] = buffer[i+offset];

    offset = 28;

    for(i=0; i<4; i++)
        pDIR->DIR_FileSize[i] = buffer[i+offset];
}

/******************************************************************************
*   getMBR
*      Sets up a structure for the Master Boot Record
*   Input:
*      pMBR - the structure to be filled
*      buffer - the data from the block read
*   Output:
*      pMBR has been filled with the MBR information.
*   Returns:
*      none
******************************************************************************/
void getMBR(P_MBR_STRUCTURE pMBR, BYTE *buffer)
{
    int i = 0;
    buffer += 0x1be; //address of first partition in MBR
    getPartition(&pMBR->partition1, buffer);
    buffer += 0x10;
    getPartition(&pMBR->partition2, buffer);
    buffer += 0x10;
    getPartition(&pMBR->partition3, buffer);
    buffer += 0x10;
    getPartition(&pMBR->partition4, buffer);
}

/******************************************************************************
*   getPartition
*      Sets up a structure for a partition
*   Input:
*      pPART - the structure to be filled
*      buffer - the data from the block read
*   Output:
*      pPART has been filled with the partition information.
*   Returns:
*      none
******************************************************************************/
void getPartition(P_PARTITION pPART, BYTE *buffer)
{
    int i = 0;

    pPART->active = buffer[0];
    pPART->startHead = buffer[1];

    for(i=0; i<2; i++)
        pPART->startSec[i] = buffer[2+i];

    pPART->type = buffer[4];
    pPART->endHead = buffer[5];

    for(i=0; i<2; i++)
        pPART->endSec[i] = buffer[6+i];

    for(i=0; i<4; i++)
        pPART->secBeforePart[i] = buffer[8+i];
    
    for(i=0; i<4; i++)
        pPART->numSec[i] = buffer[12+i];
}

/******************************************************************************
*   DumpFATStructure
*      Prints out the contents of the FAT data structure
*   Input:
*      pFAT - the structure to be printed
*   Output:
*      none
*   Returns:
*      none
******************************************************************************/
void DumpFATStructure(P_FAT_STRUCTURE pFAT)
{
    DBGMSG(1,(TEXT("RootDirsSectors = 0x%X\r\n"), pFAT->RootDirsSectors));
    DBGMSG(1,(TEXT("FirstDataSector = 0x%X\r\n"), pFAT->FirstDataSector));
    DBGMSG(1,(TEXT("FirstSectorofCluster = 0x%X\r\n"), 
	    pFAT->FirstSectorofCluster));
    DBGMSG(1,(TEXT("FirstRootDirSecNum = 0x%X\r\n"), pFAT->FirstRootDirSecNum));
    DBGMSG(1,(TEXT("FATSz = 0x%X\r\n"), pFAT->FATSz));
    DBGMSG(1,(TEXT("TotSec = 0x%X\r\n"), pFAT->TotSec));
    DBGMSG(1,(TEXT("DataSec = 0x%X\r\n"), pFAT->DataSec));
    DBGMSG(1,(TEXT("CountOfClusters = 0x%X\r\n"), pFAT->CountOfClusters));
    DBGMSG(1,(TEXT("FATType = 0x%X\r\n"), pFAT->FATType));
}

/******************************************************************************
*  findActivePartition
*     Determines which partition is the active one
*  Input:
*     pMBR - pointer to the mbr structure
*  Output:
*     none
*  Returns:
*     The number of the active partition or 0 if none are active
******************************************************************************/
int findActivePartition(P_MBR_STRUCTURE pMBR)
{
    if(pMBR->partition1.active == ACTIVE_PARTITION)
        return PARTITION_1;
    else if(pMBR->partition2.active == ACTIVE_PARTITION)
        return PARTITION_2;
    else if(pMBR->partition3.active == ACTIVE_PARTITION)
        return PARTITION_3;
    else if(pMBR->partition4.active == ACTIVE_PARTITION)
        return PARTITION_4;
    else
        return NO_PARTITION;
}

/******************************************************************************
*  computeSectorsBeforePartition
*     Converts byte array into a UINT32
*  Input:
*     pPART - the active partition
*  Output:
*     none
*  Returns:
*     The number of sectors before the partition
******************************************************************************/
XLLP_UINT32_T computeSectorsBeforePartition(P_PARTITION pPART)
{
    XLLP_UINT32_T sectors;

    sectors = pPART->secBeforePart[3];
    sectors = sectors << 8;
    sectors |= pPART->secBeforePart[2];
    sectors = sectors << 8;
    sectors |= pPART->secBeforePart[1];
    sectors = sectors << 8;
    sectors |= pPART->secBeforePart[0];

    return sectors;
}
